package composite

import "fmt"

// 组合模式（Composite Pattern）是一种结构型设计模式，用于将对象组合成树形结构以表示"部分-整体"的层次结构。
// 它使得客户端可以统一处理单个对象和组合对象，无需关心具体处理的是哪种对象。

// 模式结构
// 组件（Component）：定义所有对象的通用接口
// 叶节点（Leaf）：表示树中的叶子节点（没有子节点）
// 组合节点（Composite）：包含子组件的容器对象
// 客户端（Client）：通过组件接口操作组合结构

// 实际业务场景：电商商品分类系统
// 假设我们需要构建一个电商平台的商品分类系统：
// 分类可以包含子分类或具体商品
// 需要计算整个分类的价格
// 需要展示分类层级结构

// ProductComponent 商品分类组件
type ProductComponent interface {
	// GetPrice 计算价格
	GetPrice() float64
	Add(ProductComponent)
	Remove(ProductComponent)
	Display(indentation string)
}

// Product 叶节点-商品
type Product struct {
	Name  string
	Price float64
}

func NewProduct(name string, price float64) *Product {
	return &Product{
		Name:  name,
		Price: price,
	}
}

// GetPrice 计算商品价格
func (p *Product) GetPrice() float64 {
	return p.Price
}

// Add 商品不支持添加子分类
func (p *Product) Add(ProductComponent) {
	panic("leaf node can not add child")
}

// Remove 商品不支持移除子分类
func (p *Product) Remove(ProductComponent) {
	panic("leaf node can not remove child")
}

// Display 展示商品信息
func (p *Product) Display(indentation string) {
	fmt.Printf("%s- %s: ¥%.2f\n", indentation, p.Name, p.Price)
}

// ProductCategory 组合节点-商品分类
type ProductCategory struct {
	Name     string
	Children []ProductComponent
}

func NewProductCategory(name string) *ProductCategory {
	return &ProductCategory{
		Name:     name,
		Children: make([]ProductComponent, 0),
	}
}

// GetPrice 计算分类价格
func (pc *ProductCategory) GetPrice() float64 {
	var price float64
	for _, child := range pc.Children {
		price += child.GetPrice()
	}
	return price
}

// Add 添加子分类或商品
func (pc *ProductCategory) Add(component ProductComponent) {
	pc.Children = append(pc.Children, component)
}

// Remove 移除子分类或商品
func (pc *ProductCategory) Remove(component ProductComponent) {
	for i, child := range pc.Children {
		if child == component {
			pc.Children = append(pc.Children[:i], pc.Children[i+1:]...)
			break
		}
	}
}

// Display 展示分类层级结构
func (pc *ProductCategory) Display(indentation string) {
	fmt.Printf("%s+ %s (总价: ¥%.2f)\n", indentation, pc.Name, pc.GetPrice())
	newIndentation := indentation + "  "
	for _, child := range pc.Children {
		child.Display(newIndentation)
	}
}
